package com.yn.sample.parser;

import com.yn.sample.domain.MethodCodeStackSizeAndLocalVariablesTableSize;
import com.yn.sample.util.ParseStateEnum;
import com.yn.sample.util.FileReaderUtil;
import com.yn.sample.util.ParseEngineHelper;
import com.yn.sample.visitor.*;
import com.yn.sample.domain.ConstantPoolItem;
import com.yn.sample.domain.ClassInfo;
import com.yn.sample.domain.MethodInstructionVO;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Objects;

import static com.yn.sample.util.ParseStateEnum.METHOD_CODE_INSTRUCTION_STARTED;
import static com.yn.sample.util.ParseStateEnum.METHOD_INFO_END;

@Component
@Data
@Slf4j
public class JavapClassFileParser {

    List<String> lines;

    @Autowired
    private ClassFileInfoVisitor classFileInfoVisitor;

    @Autowired
    private ClassVersionAndFlagsInfoVisitor classVersionAndFlagsInfoVisitor;

    @Autowired
    private ClassConstantPoolInfoVisitor classConstantPoolInfoVisitor;

    @Autowired
    private ClassMethodInfoVisitor classMethodInfoVisitor;

    private int state;



    public ClassInfo parse(String javapResult) {
        lines = FileReaderUtil.convertWholeFileContent2LineArray(javapResult);
        if (CollectionUtils.isEmpty(lines)) {
            return null;
        }

        ClassMethodCodeVisitor classMethodCodeVisitor = null;
        for (int i = 0; i < lines.size(); i++) {
            String currentLine = lines.get(i);
            if (i == 0) {
                classFileInfoVisitor.visitOriginClassFileName(currentLine.split(" ")[1]);
                continue;
            } else if (i == 1) {
                String[] split = currentLine.split(";");
                String lastModifiedContent = split[0];
                String sizeContent = split[1];

                String date = lastModifiedContent.split("\\s+")[2];
                classFileInfoVisitor.visitLastModified(date);
                continue;
            }

            // 跳过不重要的先，todo

            /**
             * 当本行包含Constant pool:时，接下来就是一堆的常量：
             * Constant pool:
             *    #1 = Methodref          #6.#25         //  java/lang/Object."<init>":()V
             *    #2 = Fieldref           #5.#26         //  com/yn/sample/CheckAndSet.f:I
             * 切换状态到常量池解析开始的状态
             */
            if (currentLine.contains("Constant pool:")) {
                classConstantPoolInfoVisitor.visitConstantPoolStarted();
                state = ParseStateEnum.CONSTANT_POOL_STARTED.state;
                continue;
            }

            if (state == ParseStateEnum.CONSTANT_POOL_STARTED.state) {
                ConstantPoolItem item = ParseEngineHelper.parseConstantPoolItem(currentLine);
                if (item == null) {
                    /**
                     * 当解析失败时，在正则表达式正确的情况下，一般就是已经解析完成了所有的常量了
                     * 在javap反编译出来的文件中，一般就是解析到了如下的第二行,"{"这行：
                     *   #29 = Utf8               java/lang/Object
                     * {
                     *
                     * 这一行，下面有2种情况，一种是直接开始了一个方法，一种是开始了一个常量
                     * 开始进入了一个方法：
                     * public com.yn.sample.CheckAndSet();
                     *
                     * 开始了一个常量的部分：
                     *   public static final int constantInt;
                     *     descriptor: I
                     *     flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
                     *     ConstantValue: int 333
                     */
                    classConstantPoolInfoVisitor.visitConstantPoolEnd();

                    String nextLine = nextLine(i);
                    /**
                     * 包含了左括号和有括号，说明是一个方法
                     */
                    if (nextLine.contains("(") && nextLine.contains(")")) {
                        state = ParseStateEnum.METHOD_INFO_STARTED.state;
                    } else {
                        state = ParseStateEnum.CONSTANT_FIELD_INFO_STARTED.state;
                    }
                    continue;
                } else {
                    classConstantPoolInfoVisitor.visitConstantPoolItem(item);
                    continue;
                }
            }


            if (state == ParseStateEnum.CONSTANT_FIELD_INFO_STARTED.state) {
                if (StringUtils.isEmpty(currentLine) || "".equals(currentLine.trim())) {
                    state = ParseStateEnum.METHOD_INFO_STARTED.state;
                }
                continue;
            }

            if (state == ParseStateEnum.METHOD_INFO_STARTED.state) {

                String methodSignature = ParseEngineHelper.getMethodSignature(currentLine);
                if (methodSignature != null) {
                    classMethodInfoVisitor.visitMethodName(methodSignature);
                    // 方法名解析完成后，进入下一行，下一行为：
                    //     descriptor: ()V
                    state = ParseStateEnum.METHOD_INFO_DESCRIPTOR_STARTED.state;
                    continue;
                } else {
                    if (StringUtils.isEmpty(currentLine) || StringUtils.isEmpty(currentLine.trim())) {
                        continue;
                    }
                    if (currentLine.trim().equals("}")) {
                        state = METHOD_INFO_END.state;
                        continue;
                    }

                    /**
                     * 这里不应该会解析失败的，除非正则写错了
                     */
                    throw new RuntimeException();
                }
            } else if (state == ParseStateEnum.METHOD_INFO_DESCRIPTOR_STARTED.state) {
                String[] split = currentLine.split("\\s+");
                String descriptor = split[split.length - 1];
                classMethodInfoVisitor.visitMethodDescriptor(descriptor);
                /**
                 * 这一行访问完成后，会访问下一行：
                 * flags: ACC_PUBLIC
                 * 要切换状态为：{@link ParseStateEnum.METHOD_INFO_FLAGS_STARTED}
                 */
                state = ParseStateEnum.METHOD_INFO_FLAGS_STARTED.state;
                continue;
            } else if (state == ParseStateEnum.METHOD_INFO_FLAGS_STARTED.state) {
                String[] split = currentLine.split("\\s+");
                String flags = split[split.length - 1];
                classMethodInfoVisitor.visitFlags(flags);
                /**
                 * 这一行读取后，下几行就是这样的，代表一个方法的code区域开始了：
                 * Code:
                 *       stack=1, locals=1, args_size=1
                 *          0: aload_0
                 *          1: invokespecial #1                  // Method java/lang/Object."<init>":()V
                 *          4: return
                 *       LineNumberTable:
                 *         line 3: 0
                 *       LocalVariableTable:
                 *         Start  Length  Slot  Name   Signature
                 *             0       5     0  this   Lcom/yn/sample/CheckAndSet;
                 */
                state = ParseStateEnum.METHOD_CODE_STARTED.state;
                continue;
            }else if (state == ParseStateEnum.METHOD_CODE_STARTED.state) {
                // 遇到这行，要获取methodCode的visitor，用于接下来的visit
                if (currentLine.contains("Code:")) {
                    classMethodCodeVisitor = classMethodInfoVisitor.visitMethodCode();
                    state = ParseStateEnum.METHOD_INFO_STACK_SIZE_LOCAL_VARIABLES_SIZE_STARTED.state;
                    continue;
                }
                throw new RuntimeException();
            } else if (state == ParseStateEnum.METHOD_INFO_STACK_SIZE_LOCAL_VARIABLES_SIZE_STARTED.state) {
                MethodCodeStackSizeAndLocalVariablesTableSize vo = ParseEngineHelper.getMethodStackSizeAndLocalVariablesSize(currentLine);
                if (vo != null) {
                    classMethodCodeVisitor.visitMethodStackSizeAndLocalVariablesTableSizeAndArgsSize(vo);
                    state = METHOD_CODE_INSTRUCTION_STARTED.state;
                    continue;
                }
                throw new RuntimeException();
            } else if (state == METHOD_CODE_INSTRUCTION_STARTED.state) {
                if (currentLine.contains("LineNumberTable")) {
                    state = ParseStateEnum.METHOD_CODE_END.state;
                    continue;
                }

                MethodInstructionVO instruction = ParseEngineHelper.parseMethodByteCodeInstruction(currentLine);
                classMethodCodeVisitor.visitInstruction(instruction);
            }else if (state == ParseStateEnum.METHOD_CODE_END.state) {
                if (StringUtils.isEmpty(currentLine.trim()) || "".equals(currentLine.trim())) {
                    classMethodInfoVisitor.visitMethodEnd();
                    state = ParseStateEnum.METHOD_INFO_STARTED.state;
                    continue;
                } else if (Objects.equals(currentLine.trim(), "}")) {
                    classMethodInfoVisitor.visitMethodEnd();

                    /**
                     * 在使用jdk版本1.8.0_172-b11时，末尾是这样的，所以这样直接结束处理：
                     * ...此为方法的内容
                     *
                     * }
                     * SourceFile: "CheckAndSet.java"
                     */
                    state = METHOD_INFO_END.state;
                }
            } else if (state == METHOD_INFO_END.state) {
                log.info("finish parse work" );
                break;
            }

        }

        /**
         * 解析完成，获取需要的数据
         */
        ClassInfo classInfo = new ClassInfo();
        classInfo.setConstantPoolItems(classConstantPoolInfoVisitor.getConstantPoolItemList());
        classInfo.setMethodInfoMap(classMethodInfoVisitor.getMethodInfoMap());

        return classInfo;
    }

    public String nextLine(int currentLineIndex) {
        return lines.get(currentLineIndex + 1);
    }
    public static void main(String[] args) {
        System.out.println("ah");
    }
}
